Skip to content

Agent experience: env-var auth fallback, structured JSON errors, --agent rework#71

Open
dylanjha wants to merge 7 commits into
mainfrom
dj/default-to-existing-env-vars
Open

Agent experience: env-var auth fallback, structured JSON errors, --agent rework#71
dylanjha wants to merge 7 commits into
mainfrom
dj/default-to-existing-env-vars

Conversation

@dylanjha

@dylanjha dylanjha commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Makes the Mux CLI work well for agents and CI: credentials can come from MUX_TOKEN_ID/MUX_TOKEN_SECRET environment variables without a stored login, a global --agent flag implies JSON output everywhere, errors are machine-readable, and nothing ever blocks on stdin in non-interactive use.

Credential precedence follows the convention of the GitHub, Stripe, Vercel, AWS, and Fly CLIs: environment variables beat the stored login, and all values in a credential bundle (tokens, base URL, signing keys) come from the same source — the CLI never mixes sources.

Commits

Each commit is a reviewable theme; the branch history was rebuilt so features arrive with their safety behavior included.

  1. test: restore Mux client spy in live create tests — an unrestored module mock leaked into every test file that ran after it in the same process. Independent fix, surfaced while testing this branch.
  2. feat: agent mode with machine-readable output across commands — global --agent flag (implies JSON, sets an agent User-Agent). Commands consult wantsJson() instead of reading options.json; errors route through a JSON-aware handleCommandError; destructive commands require --force instead of prompting, with an error that names agent mode. Bulk of the diff — ~90 files of mechanical options.jsonwantsJson() conversion live here.
  3. feat: env-var credential fallback with single-source resolutionresolveCredentials prefers MUX_TOKEN_ID/MUX_TOKEN_SECRET (both required, lone variables ignored). Env credentials use MUX_BASE_URL or the default host, never a stored environment's custom host. resolveActiveEnvironment() determines which environment the active credentials operate on (one /whoami call, matched against every stored environment). A one-time stderr notice warns when env vars shadow a stored login (suppressed in agent mode).
  4. feat: login works with env credentials and fails fast in agent mode — login reads env vars when no --env-file is given, fails fast with a structured error instead of prompting in JSON/agent mode, and saves MUX_SIGNING_KEY/MUX_PRIVATE_KEY when both are present (the --env-file help text always claimed this; now it is true).
  5. fix: assets create fails fast on upload confirmation in agent mode — multi-file uploads confirmed via prompt; in JSON/agent mode this blocked on stdin. Now fails before authentication with guidance to pass -y.
  6. fix: bind signing keys and webhook state to the active environment — env-var credentials introduce a hazard this PR must handle: commands that persist local state can no longer assume the credentials belong to the logged-in environment. Before touching config, these commands verify the active environment. Credentials matching a saved environment (default or not) persist there. Credentials for an unknown environment never write to config: signing-keys create emits the private key once with saved: false, sign uses MUX_SIGNING_KEY/MUX_PRIVATE_KEY, and webhook events are stored under the real environment id from /whoami.
  7. docs: document credential sources and precedence — README section covering the resolution order, both-or-neither pairing, and the single-source rule.

QA

Agent mode & errors

mux assets list --agent                          # JSON array, no prose
mux assets delete some-id --agent                # JSON error naming --force and agent mode, exit 1
env -u MUX_TOKEN_ID -u MUX_TOKEN_SECRET mux login --agent   # structured error, no prompt, no hang

Env-var credentials

env -u MUX_TOKEN_ID -u MUX_TOKEN_SECRET mux whoami          # stored login unchanged
MUX_TOKEN_ID=<id> MUX_TOKEN_SECRET=<secret> mux whoami      # env creds win; one-line notice on stderr if a login is stored
MUX_TOKEN_ID=<id> mux whoami                                # lone variable ignored; falls back to stored login

Login

MUX_TOKEN_ID=<id> MUX_TOKEN_SECRET=<secret> mux login --json    # {"success":true,...,"source":"env"}
mux login --env-file .env                                       # saves signing keys too when the file has both

Uploads

mux assets create --upload a.mp4 --upload b.mp4 --agent     # JSON error telling you to pass -y; exits immediately
mux assets create --upload a.mp4 --upload b.mp4 --agent -y  # proceeds

Environment-state safety (use tokens for an environment that is NOT in your config)

MUX_TOKEN_ID=<B> MUX_TOKEN_SECRET=<B> mux signing-keys create --json
# → private_key in output, saved: false, config untouched
MUX_SIGNING_KEY=<key-id> MUX_PRIVATE_KEY=<base64> mux sign <playback-id>
# → signs without any stored login
MUX_TOKEN_ID=<B> MUX_TOKEN_SECRET=<B> mux webhooks listen
# → works; events stored under B's environment id, not the logged-in one

Then repeat signing-keys create with tokens for an environment that IS in your config (default or not): the key saves to that environment's entry.

Regression suite: 898 tests pass (bun test), typecheck and biome clean.

🤖 Generated with Claude Code

Comment thread src/commands/assets/create.ts
Comment thread src/lib/errors.ts
Comment thread src/commands/login.ts Outdated
Comment thread src/index.ts
Comment thread src/commands/assets/create.ts
Comment thread src/lib/mux.ts
Comment thread src/lib/mux.ts
Comment thread src/commands/sign.ts
Comment thread src/commands/assets/delete.ts
Comment thread src/lib/mux.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d25cc28. Configure here.

Comment thread src/commands/login.ts
environmentId: validation.environmentId,
...(baseUrl !== DEFAULT_BASE_URL && { baseUrl }),
...signingKeys,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Login drops saved signing keys

Medium Severity

mux login now persists MUX_SIGNING_KEY / MUX_PRIVATE_KEY, but setEnvironment replaces the whole environment object. A later token-only login (the common agent path with just MUX_TOKEN_ID / MUX_TOKEN_SECRET) omits signingKeys and silently clears any keys saved earlier, including ones from signing-keys create. forwardUrl is wiped the same way. Unlike signing-keys create, which merges via a spread of the existing environment, login does not preserve prior fields.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d25cc28. Configure here.

dylanjha and others added 7 commits July 23, 2026 13:29
The unrestored module spy leaked a mock client into every test file
that ran afterward in the same process.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a global --agent flag that implies JSON output and identifies as an
agent in the User-Agent header. Commands consult wantsJson() (true for
--json or agent mode) instead of reading options.json directly, errors
route through a JSON-aware handleCommandError, the top-level error
handler stays machine-readable, and destructive commands require
--force with guidance that names agent mode instead of prompting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolve credentials from MUX_TOKEN_ID/MUX_TOKEN_SECRET (both required)
ahead of the stored login, matching gh/Stripe/Vercel CLI conventions.
All values in a bundle come from one source: env credentials use
MUX_BASE_URL or the default host, never a stored environment's baseUrl.
resolveActiveEnvironment identifies which environment the active
credentials operate on (one /whoami call for env credentials, matched
against every stored environment) so commands can persist state safely.
A one-time stderr notice warns when env vars shadow a stored login.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Login reads MUX_TOKEN_ID/MUX_TOKEN_SECRET when no --env-file is given,
fails fast with a structured error instead of prompting in JSON or
agent mode, routes errors through handleCommandError, and saves
MUX_SIGNING_KEY/MUX_PRIVATE_KEY to the environment when both are
present (making the long-standing --env-file help text accurate).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Multi-file uploads blocked on an interactive confirmation prompt in
non-interactive runs. Fail before authentication with guidance to pass
-y/--yes when machine-readable output is active.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
signing-keys create persists only to a stored environment that matches
the active credentials; otherwise the private key is emitted once with
saved: false and MUX_SIGNING_KEY/MUX_PRIVATE_KEY guidance. sign honors
those env vars and only falls back to a matching stored environment's
keys. The webhooks family works with env credentials alone, stores and
queries events by the environment the credentials actually belong to,
and persists forward URLs only to a matching stored environment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dylanjha
dylanjha force-pushed the dj/default-to-existing-env-vars branch from d25cc28 to fe5d6e2 Compare July 23, 2026 20:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant